#ifndef _M_CONNECTION_H_
#define _M_CONNECTION_H_

#include "muduo/proto/dispatcher.h"
#include "muduo/proto/codec.h"
#include "muduo/base/Logging.h"
#include "muduo/base/Mutex.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpClient.h"
#include "muduo/net/EventLoopThread.h"
#include "muduo/base/CountDownLatch.h"

#include "channel.hpp"
#include "asyncWorker.hpp"

#include "../mqcommon/logger.hpp"


namespace MyRabbitMQ
{
    class Connection
    {
    public:
        using ptr = std::shared_ptr<Connection>;

        Connection(const std::string& sip, int port, const AsyncWorker::ptr& works)
        :m_works(works),
        m_countdownLatch(1),
        m_client(works->m_eventLoopThread.startLoop(),muduo::net::InetAddress(sip,port),"Client"),
        m_dispatcher(std::bind(&Connection::onUnknownMessage,this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3)), //注册一个默认处理函数
        m_codec(std::make_shared<ProtobufCodec>(std::bind(&ProtobufDispatcher::onProtobufMessage,&this->m_dispatcher,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3))),
        //协议处理器处理protobuf格式的完整报文, 解析后将数据交由事件派发器进行处理, 事件派发器根据数据类型选择对应函数进行处理
        m_channels(std::make_shared<ChannelManager>())
        {
            //向事件分发器中注册特定事件处理函数(优先执行该逻辑, 不应颠倒顺序, 原因在注意事项有)
            m_dispatcher.registerMessageCallback<protoMQ::basicCommonResponse>(std::bind(&Connection::basicCommonResponse,this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
            m_dispatcher.registerMessageCallback<protoMQ::basicPushResponse>(std::bind(&Connection::basicPushResponse,this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));

            //连接到来时, 处理连接逻辑
            m_client.setConnectionCallback(std::bind(&Connection::onConnection,this,std::placeholders::_1)); 
            //消息到来时, 处理消息逻辑(这里将未知消息转由protobufCodec进行解析,得到完整报文)
            m_client.setMessageCallback(std::bind(&ProtobufCodec::onMessage,m_codec,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3)); 

            m_client.connect(); //客户端试图与指定ip地址与端口号下的进程建立连接
            sleep(1);
            m_countdownLatch.wait(); //阻塞等待, 直到条件满足(这里的条件是指连接建立成功)
        }


        //创建信道
        Channel::ptr createChannel()
        {
            //创建信道对象
            auto ptr = this->m_channels->createChannel(this->m_conn,this->m_codec);

            //通知服务器, 客户端创建了信道对象
            if(!ptr->openChannel())
            {
                LOG(logLevel::ERROR) << "执行失败:信道开启失败!" << std::endl;
                return Channel::ptr();
            }

            LOG(logLevel::INFO) << "创建了信道对象" << ptr->getChannelId() << std::endl; 
            return ptr;
        }

        //删除信道
        void deleteChannel(const std::string& cid)
        {
            //先判断是否存在
            auto ptr = this->m_channels->getChannnel(cid);
            if(ptr == Channel::ptr()) return;

            //通知服务器关闭信道
            ptr->closeChannel();

            //删除信道对象
            this->m_channels->deleteChannel(cid);

            LOG(logLevel::INFO) << "删除了信道对象" << cid << std::endl; 
            return;
        }

    private:

        muduo::CountDownLatch m_countdownLatch; //同步机制前置
        muduo::net::TcpConnectionPtr m_conn; //单个连接
        muduo::net::TcpClient m_client; //单个连接对应的客户端
        ProtobufDispatcher m_dispatcher; //请求分发器
        ProtobufCodecPtr m_codec; //协议处理器

        AsyncWorker::ptr m_works; //异步工作模块
        ChannelManager::ptr m_channels; //信道管理模块 

        //默认消息处理函数
        void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn, const MessagePtr& message, muduo::Timestamp) 
        {
            LOG(logLevel::ERROR) << "客户端收到未知类型的消息" << std::endl;
            LOG_INFO << "onUnknownMessage: " << message->GetTypeName();
            conn->shutdown();
        }
        
         //默认连接处理函数
        void onConnection(const muduo::net::TcpConnectionPtr& conn)
        {
            if(conn->connected())
            {
                LOG(logLevel::INFO) <<"连接建立成功" << std::endl;
                m_conn = conn;
                m_countdownLatch.countDown(); //计数减少1, 唤醒阻塞, 这里必须在最后,因为只有操作完毕复制了m_conn才能唤醒上边的等待,不能先唤醒再操作
            }
            else
            {
                //如果无连接, 则同步重置
                m_conn.reset();

                LOG(logLevel::WARNING) <<"无有效连接, 连接对象已重置" << std::endl;
            }
        }

        //业务处理: 普通响应
        void basicCommonResponse(const muduo::net::TcpConnectionPtr& conn, const basicCommonResponsePtr& message, muduo::Timestamp)
        {
            //获取对应信道
            Channel::ptr channel = m_channels->getChannnel(message->channel_id());

            if(channel == Channel::ptr())
            {
                LOG(logLevel::ERROR) << "执行失败:未找到信道" << message->channel_id() << std::endl;
                return;
            }

            //封装异步任务到线程池中
            m_works->m_pool.push([channel,message](){channel->pushBasicCommonResponse(message);});
            //channel->pushBasicCommonResponse(message);
        }

        //业务处理: 消息推送
        void basicPushResponse(const muduo::net::TcpConnectionPtr& conn, const basicPushResponsePtr& message, muduo::Timestamp)
        {
            //获取对应信道
            Channel::ptr channel = m_channels->getChannnel(message->channel_id());
            if(channel == Channel::ptr())
            {
                LOG(logLevel::ERROR) << "执行失败:未找到信道" << message->channel_id() << std::endl;
                return;
            }

            //封装异步任务到线程池中
            //这里不能使用 m_works->m_pool.push([&channel,message](){channel->handleBasicPushResponse(message);});
            m_works->m_pool.push([channel,message](){channel->handleBasicPushResponse(message);});
        }
    };

};

#endif